package com.zynn.api.yinian.zuul.service;

import com.alibaba.fastjson.JSONObject;
import com.google.common.io.CharStreams;
import com.netflix.zuul.context.RequestContext;
import com.zynn.common.core.utils.JsonUtil;
import com.zynn.common.core.utils.SequenceUtil;
import com.zynn.common.core.utils.ThreadPoolFactory;
import com.zynn.common.pojo.dto.zuul.LogInfoDTO;
import com.zynn.common.pojo.result.Result;
import com.zynn.common.pojo.result.ResultEnum;
import com.zynn.feign.module.es.SyncEventFeignService;
import com.zynn.message.queue.constant.MqQueueNameConstant;
import com.zynn.message.queue.service.SenderService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.netflix.ribbon.apache.RibbonApacheHttpResponse;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;

/**
 * 日志处理
 */
@Service
@Slf4j
public class LogService {

    @Autowired
    private SyncEventFeignService syncEventFeignService;

    @Autowired
    private SendMailService sendMailService;

    private final String LOG_INFO = "logInfo";

    private static ExecutorService executor = ThreadPoolFactory.createThreadPoolExecutor("zuul日志记录", 100);

    @Autowired
    private SenderService senderService;

    public void handlePreLog(RequestContext requestContext) {
        LogInfoDTO logInfo = new LogInfoDTO();
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        logInfo.setCreateTime(format.format(new Date()));
        logInfo.setStartTime(System.currentTimeMillis());
        HttpServletRequest request = requestContext.getRequest();
        String requestUrl = request.getRequestURL().toString();
        String method = request.getMethod();
        String params = JSONObject.toJSONString(request.getParameterMap());
        String version = request.getHeader("version");
        String source = request.getHeader("source");
        Long requestId = SequenceUtil.getId();
        requestContext.addZuulRequestHeader("requestid", requestId.toString());
        logInfo.setRequestId(requestId);
        logInfo.setJwt(request.getHeader("USER_INFO_KEY"));
        logInfo.setUserId(requestContext.getZuulRequestHeaders().get("loginuserid"));
        logInfo.setUserAgent(request.getHeader("user-agent"));
        logInfo.setRequestUrl(requestUrl);
        logInfo.setMethod(method);
        logInfo.setParams(params);
        logInfo.setVersion(version);
        logInfo.setSource(source);
        requestContext.put(LOG_INFO, logInfo);
    }

    /**
     * Post请求日志处理
     * @param requestContext 上下文内容
     */
    public void handlePostLog(RequestContext requestContext) {
        LogInfoDTO logInfo = (LogInfoDTO) requestContext.get(LOG_INFO);
        logInfo.setEndTime(System.currentTimeMillis());
        logInfo.setTime(logInfo.getEndTime() - logInfo.getStartTime());
        logInfo.setResponseStatusCode(requestContext.getResponseStatusCode());

        RibbonApacheHttpResponse ribbonResponse = (RibbonApacheHttpResponse) requestContext.get("ribbonResponse");
        if(ribbonResponse!=null) {
            String redirectHost = ribbonResponse.getRequestedURI().getHost();
            int redirectPort = ribbonResponse.getRequestedURI().getPort();
            logInfo.setRedirectHost(redirectHost);
            logInfo.setRedirectPort(redirectPort);
        }

        if (StringUtils .isNotEmpty(requestContext.getResponseBody())) {
            logInfo.setResponseData(requestContext.getResponseBody());
        }

        Throwable throwable = requestContext.getThrowable();
        if (throwable != null) {
            Throwable cause = throwable.getCause();
            while (cause.getCause()!=null) {
                cause = cause.getCause();
            }

            StringWriter sw = new StringWriter();
            cause.printStackTrace(new PrintWriter(sw, true));
            logInfo.setException(sw.getBuffer().toString());
            // 打印zuul抛出的异常
            log.error("zuul异常捕获", throwable);
        }
        if (logInfo.getResponseStatusCode() == HttpStatus.SC_OK) {
            /// 避免日志输出太多
            //log.info(logInfo.toString());
        } else {
            if(throwable == null) {
                //打印微服务抛出的异常
                log.error(logInfo.toString());
            }
            if(StringUtils.isEmpty(logInfo.getResponseData()) && requestContext.getResponseDataStream()!=null) {
                try (final InputStream responseDataStream = requestContext.getResponseDataStream()) {
                    final String responseData = CharStreams.toString(new InputStreamReader(responseDataStream, "UTF-8"));
                    requestContext.setResponseBody(responseData);
                    logInfo.setResponseData(responseData);
                } catch (IOException e) {
                    log.error("Error reading body",e);
                }
            }
        }


        /**
         * 1.开启线程 同步zuul Log到es里面
         * 2.发送COLLECT_ROUTING_LOG消息 消费者发送邮件到钉钉邮箱记录
         */
        /// executor.execute(()->syncEventFeignService.syncLog(logInfo));
        // senderService.send(MqQueueNameConstant.COLLECT_ROUTING_LOG, JsonUtil.object2json(logInfo));
    }


    /**
     * 消费日志MQ 发送邮件到钉钉邮箱
     * @param logInfoJson
     */
    @JmsListener(destination = MqQueueNameConstant.COLLECT_ROUTING_LOG)
    public void collectRoutingLog(String logInfoJson) {
        LogInfoDTO logInfo = (LogInfoDTO) JsonUtil.json2object(logInfoJson, LogInfoDTO.class);
        int time = 5000;
        String onLineKeyWord = "api2";
        if(logInfo.getRequestUrl().contains(onLineKeyWord)) {
            if(logInfo.getResponseStatusCode()!=HttpStatus.SC_OK ) {
                Result result = (Result)JsonUtil.json2object(logInfo.getResponseData(), Result.class);
                if(result!=null && result.getCode() == ResultEnum.UN_AUTH.getCode()) {
                    return;
                }
                sendMailService.sendSimpleMail("接口报错"+logInfo.getRequestUrl(), logInfo.toString());
            } else if(logInfo.getTime()>time) {
                sendMailService.sendSimpleMail("接口耗时"+logInfo.getTime()+","+logInfo.getRequestUrl(), logInfo.toString());
            }
        }

    }
}
